package com.shouru.server;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 项目修改检测器
 * @author liudajiang 
 * @create_date 2016年7月14日
 *
 */
public  class ChangeScanner {
	/**
	 * 检测间隔时间
	 */
	private Long scanIntervalSeconds;
	
	/**
	 * 调度线程池
	 */
	private ScheduledExecutorService scheduledExecutorService;
	/**
	 * 修改事件处理器
	 */
	private AppChangeHandler changeHandler;
	/**
	 * 排除检测解析器
	 */
	private ExcudeScanResolver excudeScanResolver;
	/**
	 * 修改触发文件<br/>
	 * 此文件设置后ExcudeScanResolver将失效
	 */
	private File triggerFile;
	/**
	 * 上次检测结果<br/>
	 * 		key:文件全路径<br/>
	 * 		value:文件最后修改时间
	 */
	private Map<String,Long> lastResult=new HashMap<String, Long>();
	/**
	 * 本次检测结果<br/>
	 * 		key:文件全路径<br/>
	 * 		value:文件最后修改时间
	 */
	private Map<String,Long> currentResult=new HashMap<String, Long>();

	public ChangeScanner() {
	}

	public void start() {
		ScannerTask scannerTask=new ScannerTask();
		scannerTask.addScanFile(AbstarctServer.getRootClassFile());
		getScheduledExecutorService().scheduleAtFixedRate(scannerTask, 0, scanIntervalSeconds,
				TimeUnit.SECONDS);
	}
	
	private void onchange(){
		if(changeHandler!=null){
			changeHandler.onChange();
		}
	}

	

	private class ScannerTask implements Runnable {
		
		List<File> files=new LinkedList<File>();
		

		/**
		 * 增加检测文件<br/>
		 * 如果文件为一个目录，会递归检测目录下所有文件
		 * @param file
		 * @create_date 2016年7月14日
		 */
		public void addScanFile(File file){
			files.add(file);
		}
		public List<File> fromScanPath(String path){
			List<File> files=new ArrayList<File>();
			String[] paths=null;
			if(path.indexOf(String.valueOf(File.pathSeparator))!=-1){
				paths=path.split(File.pathSeparator);
			}else{
				paths=new String[]{path};
			}
			for(String eachPath:paths){
				files.add(new File(eachPath));
			}
			return files;
		}

		@Override
		public void run() {
			scan();
			if(isChanged()){
				System.out.println("检测到代码修改");
				onchange();
			}
			lastResult.clear();
			lastResult.putAll(currentResult);
			currentResult.clear();
		}
		
		private void scan() {
			List<File> scanFiles=new ArrayList<File>();
			if(triggerFile!=null){
				scanFiles.add(triggerFile);
			}else{
				scanFiles.addAll(files);
				//检测java classpath,用来检测项目依赖的 项目 或 类库
				scanFiles.addAll(fromScanPath(System.getProperty("java.class.path")));
			}
			Set<String> fileSet=new HashSet<String>();
			
			for(File file:scanFiles){
				if(fileSet.contains(file.getAbsolutePath())){
					continue;
				}
				try{
					scanFile(file);
				}catch(Exception e){
					System.out.println("检测文件："+file.getAbsolutePath()+"失败");
					e.printStackTrace();
				}
				fileSet.add(file.getAbsolutePath());
			}
		}

		/**
		 * 与上次检测结果对比是否以改变
		 * @return true:不同 false:相同
		 * @create_date 2016年7月11日
		 */
		private boolean isChanged() {
			boolean result=false;
			for(Map.Entry<String,Long> entry:currentResult.entrySet()){
				Long lastmodify=lastResult.get(entry.getKey());
				if(lastmodify==null){
					return false;
				}
				if(!entry.getValue().equals(lastmodify)){
					System.out.println("检测到修改文件："+entry.getKey());
					if(!result){
						result=true;
					}
				}
			}
			return result;
		}

		/**
		 * 获取文件的修改时间<br/>
		 * 如果文件是目录，会递归获取目录下所有文件
		 * @param file
		 * @create_date 2016年7月14日
		 */
		private void scanFile(File file){
			if(file==null||!file.exists()){
				return;
			}
			if(file.isDirectory()){
				for(File subFile:file.listFiles()){
					scanFile(subFile);
				}
				return;
			}
			if(triggerFile==null&&excudeFile(file)){
				return;
			}
			getCurrentResult().put(file.getAbsolutePath(),file.lastModified());
		}

		private boolean excudeFile(File file) {
			if(getExcudeScanResolver()!=null&&getExcudeScanResolver().isExcude(file)){
				return true;
			}
			return false;
		}
		public Map<String, Long> getCurrentResult() {
			if(currentResult==null){
				currentResult=new HashMap<String, Long>();
			}
			return currentResult;
		}
		
	}

	public Long getScanIntervalSeconds() {
		return scanIntervalSeconds;
	}

	public ChangeScanner setScanIntervalSeconds(Long scanIntervalSeconds) {
		this.scanIntervalSeconds = scanIntervalSeconds;
		return this;
	}

	public ScheduledExecutorService getScheduledExecutorService() {
		if (scheduledExecutorService == null) {
			scheduledExecutorService = Executors.newScheduledThreadPool(1);
		}
		return scheduledExecutorService;
	}

	public void setScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
		this.scheduledExecutorService = scheduledExecutorService;
	}

	public ChangeScanner setOnchange(AppChangeHandler handler) {
		this.changeHandler=handler;
		return this;
	}
	
	public interface AppChangeHandler{
		public void onChange();
	}

	public ExcudeScanResolver getExcudeScanResolver() {
		return excudeScanResolver;
	}

	public ChangeScanner setExcudeScanResolver(ExcudeScanResolver excudeScanResolver) {
		this.excudeScanResolver = excudeScanResolver;
		this.excudeScanResolver.setLastResult(lastResult);
		return this;
	}

	public ChangeScanner setTriggerFile(File triggerFile) {
		this.triggerFile = triggerFile;
		return this;
	}
}
